package com.simple.car.loader.server.distapch;

import com.google.gson.Gson;
import com.simple.car.loader.server.annotation.Controller;
import com.simple.car.loader.server.annotation.Function;
import com.simple.car.loader.server.annotation.Init;
import com.simple.car.loader.server.annotation.Loader;
import com.simple.car.loader.server.annotation.Mapping;
import com.simple.car.loader.server.entity.Apply;
import com.simple.car.loader.server.entity.Executor;
import com.simple.car.loader.server.manager.impl.ControllerManager;
import com.simple.car.loader.server.parser.AutoFind;
import com.simple.car.loader.server.tools.ReflectionsTool;

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

@Function
public class AutoDispatch implements Dispatch<Apply> {

    private Map<String, Executor> executors;

    private Gson gson = new Gson();

    @Loader
    private AutoFind autoFind;

    @Init
    public void init() throws IllegalAccessException, InstantiationException {
        Set<Class<?>> classes = ReflectionsTool.getClassByAnnotationClass(Controller.class);
        if (classes == null || classes.isEmpty()) {
            return;
        }
        executors = new HashMap<>();
        for (Class<?> aClass : classes) {
            Controller controller = aClass.getDeclaredAnnotation(Controller.class);
            if (controller == null) {
                continue;
            }
            Object instance = aClass.newInstance();
            ControllerManager.getInstance().register(aClass.getName(), instance);
            String value = controller.value();
            Method[] methods = aClass.getDeclaredMethods();
            for (Method method : methods) {
                Mapping mapping = method.getDeclaredAnnotation(Mapping.class);
                if (mapping == null) {
                    continue;
                }
                String res = mapping.value();
                res = value.concat(res);
                Executor executor = Executor.fromMethod(instance, method);
                executors.put(res, executor);
            }
        }
    }

    @Override
    public void dispatch(Socket socket, Apply apply) {
        Executor executor = executors.get(apply.getRes());
        if (executor == null){
            Apply a = new Apply();
            a.setType("json");
            Map<String, String> map = new HashMap<>();
            map.put("error", String.format("没有资源：%s", apply.getRes()));
            a.setData(gson.toJson(map).getBytes(StandardCharsets.UTF_8));
            sendResult(socket, a);
            return;
        }
        Method method = executor.getMethod();
        Object[] objects = autoFind.find(socket, apply, method);
        try {
            Object invoke = method.invoke(executor.getObject(), objects);
            if (invoke != null) {
                sendResult(socket, invoke);
            }
        } catch (IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    protected void sendResult(Socket socket, Object o) {
        String json = gson.toJson(o);
        byte[] bytes = json.getBytes(StandardCharsets.UTF_8);
        try (
                ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                DataOutputStream dataOutputStream = new DataOutputStream(byteArrayOutputStream)
        ) {
            dataOutputStream.writeInt(bytes.length);
            dataOutputStream.write(bytes);
            dataOutputStream.flush();
            socket.getOutputStream().write(byteArrayOutputStream.toByteArray());
            socket.getOutputStream().flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
